extern crate round;
mod stack;
mod operator;
mod expression;
use operator::{Operator, CompareSympol};
use expression::{Expression, ExpToken};
use round::{round};


pub fn evaluate_expression(exp: &str) -> Result<f64, String> {

    let mut op_stack: stack::Stack<char> = stack::Stack::new();
    let mut num_stack: stack::Stack<f64> = stack::Stack::new();
    // let mut last_type = lastOpType::Init;
    let operator = Operator::new();
    let mut aexp = Expression::new(exp.into())?;
    let mut el_option = aexp.get_el();
    while el_option.is_some() {
        let el = el_option.clone().unwrap();
        match el {
            ExpToken::Opchar(cx) => {
                match op_stack.get_top() {
                    Some(left_char) => {
                        match operator.compare_priority(&left_char, &cx)? {
                            CompareSympol::LT => {
                                if cx == ')' {
                                    let theta = check_operator(&op_stack.pop())?;
                                    let right_num = check_operator_num(&num_stack.pop())?;
                                    let left_num = check_operator_num(&num_stack.pop())?;
                                    num_stack.push(operator.operate(&theta, &left_num, &right_num)?);
                                } else {
                                    op_stack.push(cx);
                                    el_option = aexp.get_el();
                                }
                            },
                            CompareSympol::EQ => {
                                if cx == ')' {
                                    op_stack.pop();
                                    el_option = aexp.get_el();
                                } else {
                                    let theta = check_operator(&op_stack.pop())?;
                                    let right_num = check_operator_num(&num_stack.pop())?;
                                    let left_num = check_operator_num(&num_stack.pop())?;
                                    num_stack.push(operator.operate(&theta, &left_num, &right_num)?);
                                }
                            },
                            CompareSympol::GT => {
                                if *left_char == '(' {
                                    op_stack.push(cx);
                                    el_option = aexp.get_el();
                                } else {
                                    let theta = check_operator(&op_stack.pop())?;
                                    let right_num = check_operator_num(&num_stack.pop())?;
                                    let left_num = check_operator_num(&num_stack.pop())?;
                                    num_stack.push(operator.operate(&theta, &left_num, &right_num)?);
                                }
                            },
                        }
                    },
                    None => {
                        op_stack.push(cx);
                        el_option = aexp.get_el();
                    }
                }
            },
            ExpToken::Opnum(nx) => {
                num_stack.push(nx);
                el_option = aexp.get_el();
            }
        }
    }
    while op_stack.get_top().is_some() {
        let theta = check_operator(&op_stack.pop())?;
        let right_num = check_operator_num(&num_stack.pop())?;
        let left_num = check_operator_num(&num_stack.pop())?;
        num_stack.push(operator.operate(&theta, &left_num, &right_num)?);
    }
    match num_stack.pop() {
        Some(x) => {
            Ok(round(x, 8))
        },
        None => {
            Err(format!("evaluate err"))
        },
    }
}

fn check_operator(op_option: &Option<char>) -> Result<char, String> {
    match op_option {
        Some(x) => {
            let operator = Operator::new();
            if operator.has(&x) {
                Ok(*x)
            } else {
                Err(format!("operator char({}) is not support", x))
            }
        },
        None => {
            Err(format!("operator option is none"))
        }
    }
}


fn check_operator_num(op_option: &Option<f64>) -> Result<f64, String> {
    match op_option {
        Some(x) => {
            Ok(*x)
        },
        None => {
            Err(format!("operator num is none"))
        }
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_evaluate_expression() {
        let result = evaluate_expression("1+2+3").unwrap();
        assert_eq!(result, 6.0);
        let result = evaluate_expression("1+2*3").unwrap();
        assert_eq!(result, 7.0);
        let result = evaluate_expression("1+2*3-1").unwrap();
        assert_eq!(result, 6.0);
        let result = evaluate_expression("1+2*3-1 / 1").unwrap();
        assert_eq!(result, 6.0);
        let result = evaluate_expression("1+(2*3)-1 / 1").unwrap();
        assert_eq!(result, 6.0);
        let result = evaluate_expression("(1+3 - 2)*3-1 / 1").unwrap();
        assert_eq!(result, 5.0);
        let result = evaluate_expression("(1+3 * 2)*3-1 / 1").unwrap();
        assert_eq!(result, 20.0);
        let result = evaluate_expression("(1.0+3 * 2)*3-1 / 1").unwrap();
        assert_eq!(result, 20.0);
        let result = evaluate_expression("1 / 3").unwrap();
        assert_eq!(result, 0.33333333);
        let result = evaluate_expression("13").unwrap();
        assert_eq!(result, 13.0);
        let result = evaluate_expression("((1.0+3 * 2)*3-1 / 1");
        match result {
            Ok(_x) => {
                assert!(false);
            },
            Err(s) => {
                assert!(true);
            }
        }
        let result = evaluate_expression("13++");
        match result {
            Ok(_x) => {
                assert!(false);
            },
            Err(s) => {
                assert!(true);
            }
        }
        // assert!(true);
    }
}
